# -*- coding: utf8 -*-

import urllib2
from urlparse import urlparse
from BeautifulSoup import BeautifulSoup
from puppy.model import Query, Response

def search(query):
  """Search function for OpenSearch Compliant Service.
  
  If a specific search engine module does not exist, a search service takes the website address and attempts to discover an OpenSearch Description document.  From this document, the service can determine the search template to be used for this website.
  
  Parameters:
  
  * query (puppy.model.OpenSearch.Query)
  
  Returns:
  
  * puppy.model.OpenSearch.Response
  
  Raises:
  
  * urllib2.URLError
  
  """
  
  def discover(uri):
    """
    Discover if website supports OpenSearch.
    
    Loads the URI into memory and searches for a <link rel='search'> pattern.  If found, unfortunately there are no rules about whether the OpenSearch Description document is linked using an absolute or relative path.  Therefore, only a simple test is used here to determine if the link starts with 'http://'.
    
    Parameters:
    
    * uri (str): website address to test for OpenSearch compliance
    
    Returns:
    
    * str: address of OpenSearch Description document
      
    Raises:
    
    * Exception: if website does not appear to support OpenSearch
    * urllib2.URLError: if website cannot be reached
    """
    
    try:
      user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
      headers = {'User-Agent': user_agent}
      request = urllib2.Request(uri, headers=headers)
      response = urllib2.urlopen(request)
      os_link = BeautifulSoup(response.read()).find('link', rel='search')
      if os_link == None:
        raise Exception("{0} does not support OpenSearch".format(uri))
      elif os_link.attrMap.has_key('type'):
        if os_link.attrMap['type'] == "application/opensearchdescription+xml":
          if os_link.attrMap['href'][0:4] == 'http':
            return os_link.attrMap['href']
          else:
            parsed = urlparse(uri)
            scheme, netloc = parsed.scheme, parsed.netloc
            return "".join((scheme, '://', netloc, os_link.attrMap['href']))
        else:
          raise Exception("{0} does not support OpenSearch".format(uri))
      else:
        raise Exception("{0} does not support OpenSearch".format(uri))
    except urllib2.URLError, e:
      print "Could not retrieve {0}: {1}".format(uri, e)
  
  
  def find_pattern(uri):
    """
    Find search pattern from OpenSearch Description document.
    
    Each OpenSearch Description document has a template that tells a client how to perform a search.  This function searches for the <url> tag and returns the template attribute.
    
    Parameters:
    
      * uri (str): address of the OpenSearch Description document
    
    Returns:
    
      * str: search template
    
    Raises:
    
      * urllib2.URLError: if website cannot be reached
    """
    try:
      user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
      headers = {'User-Agent': user_agent}
      request = urllib2.Request(uri, headers=headers)
      response = urllib2.urlopen(request)
      os_url = BeautifulSoup(response.read()).find('url', type="application/rss+xml")
      return os_url.attrMap['template']
    except urllib2.URLError, e:
      print "Could not retrieve OpenSearch Description from {0}: {1}".format(uri, e)
  
  
  url = find_pattern(discover(query.service)).replace('{searchTerms}', urllib2.quote(query.search_terms))
  try:
    response = urllib2.urlopen(url)
    return Response.parse_xml(response.read())
  except urllib2.URLError, e:
    print "Error in Search Service: {0}/OpenSearch search failed".format(uri)
